/***********************************************************************
 basic-server.cpp - Implements a fairly basic single-threaded Winsock 
    server program that waits for a connection, accepts it, echoes back
    any data it receives, and then goes back to listening when the
    client drops the connection.

 Compiling:
    VC++: cl -GX basic-server.cpp main.cpp ws-util.cpp wsock32.lib
    BC++: bcc32 basic-server.cpp main.cpp ws-util.cpp
    
 This program is hereby released into the public domain.  There is
 ABSOLUTELY NO WARRANTY WHATSOEVER for this product.  Caveat hacker.
***********************************************************************/

#include "ws-util.h"
#include <winsock.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <regex>
#include <sys\types.h> 
#include <sys\stat.h> 

using namespace std;
using namespace std::tr1;


//// Constants /////////////////////////////////////////////////////////

const int kBufferSize = 1024;
string basedir = "C:\\xampp\\htdocs";
string index = "index.html";
string filedivider = "\\";
        

//// Prototypes ////////////////////////////////////////////////////////

SOCKET SetUpListener(const char* pcAddress, int nPort);
void AcceptConnections(SOCKET ListeningSocket);
bool EchoIncomingPackets(SOCKET sd);
__int64 FileSize64( const char * szFileName );

//// DoWinsock /////////////////////////////////////////////////////////
// The module's driver function -- we just call other functions and
// interpret their results.

int DoWinsock(const char* pcAddress, int nPort)
{
    cout << "Establishing the listener..." << endl;
    SOCKET ListeningSocket = SetUpListener(pcAddress, htons(nPort));
    if (ListeningSocket == INVALID_SOCKET) {
        cout << endl << WSAGetLastErrorMessage("establish listener") << 
                endl;
        return 3;
    }

    cout << "Waiting for connections..." << flush;
    while (1) {
        AcceptConnections(ListeningSocket);
        cout << "Acceptor restarting..." << endl;
    }

#if defined(_MSC_VER)
    return 0;   // warning eater
#endif
}


//// SetUpListener /////////////////////////////////////////////////////
// Sets up a listener on the given interface and port, returning the
// listening socket if successful; if not, returns INVALID_SOCKET.

SOCKET SetUpListener(const char* pcAddress, int nPort)
{
    u_long nInterfaceAddr = inet_addr(pcAddress);
    if (nInterfaceAddr != INADDR_NONE) {
        SOCKET sd = socket(AF_INET, SOCK_STREAM, 0);
        if (sd != INVALID_SOCKET) {
            sockaddr_in sinInterface;
            sinInterface.sin_family = AF_INET;
            sinInterface.sin_addr.s_addr = nInterfaceAddr;
            sinInterface.sin_port = nPort;
            if (bind(sd, (sockaddr*)&sinInterface, 
                    sizeof(sockaddr_in)) != SOCKET_ERROR) {
                listen(sd, SOMAXCONN);
                return sd;
            }
            else {
                cerr << WSAGetLastErrorMessage("bind() failed") <<
                        endl;
            }
        }
    }

    return INVALID_SOCKET;
}


//// EchoHandler ///////////////////////////////////////////////////////
// Handles the incoming data by reflecting it back to the sender.

DWORD WINAPI EchoHandler(void* sd_) 
{
    int nRetval = 0;
    SOCKET sd = (SOCKET)sd_;

    if (!EchoIncomingPackets(sd)) {
        cerr << endl << WSAGetLastErrorMessage(
                "Echo incoming packets failed") << endl;
        nRetval = 3;
    }

    cout << "Shutting connection down..." << flush;
    if (ShutdownConnection(sd)) {
        cout << "Connection is down." << endl;
    }
    else {
        cerr << endl << WSAGetLastErrorMessage(
                "Connection shutdown failed") << endl;
        nRetval = 3;
    }

    return nRetval;
}


//// AcceptConnections /////////////////////////////////////////////////
// Spins forever waiting for connections.  For each one that comes in, 
// we create a thread to handle it and go back to waiting for
// connections.  If an error occurs, we return.

void AcceptConnections(SOCKET ListeningSocket)
{
    sockaddr_in sinRemote;
    int nAddrSize = sizeof(sinRemote);

    while (1) {
        SOCKET sd = accept(ListeningSocket, (sockaddr*)&sinRemote,
                &nAddrSize);
        if (sd != INVALID_SOCKET) {
            cout << "Accepted connection from " <<
                    inet_ntoa(sinRemote.sin_addr) << ":" <<
                    ntohs(sinRemote.sin_port) << "." <<
                    endl;

            DWORD nThreadID;
            CreateThread(0, 0, EchoHandler, (void*)sd, 0, &nThreadID);
        }
        else {
            cerr << WSAGetLastErrorMessage("accept() failed") << 
                    endl;
            return;
        }
    }
}


//// EchoIncomingPackets ///////////////////////////////////////////////
// Bounces any incoming packets back to the client.  We return false
// on errors, or true if the client closed the socket normally.

bool EchoIncomingPackets(SOCKET sd)
{
    // Read data from client
    char acReadBuffer[kBufferSize];
    int nReadBytes;
    do {
        nReadBytes = recv(sd, acReadBuffer, kBufferSize, 0);
        if (nReadBytes > 0) {
            cout << "Received " << nReadBytes << 
                    " bytes from client." << endl;
			string message = string(acReadBuffer,nReadBytes);
			//cout << message << "\n";
			string line;
			istringstream smessage( message );
			int i=0;
			regex getPattern("GET (.*) HTTP/(.*)",std::tr1::regex_constants::icase);
			regex blankline("^$");

			smatch result;
			string file = index;
			int moveon = 0;
			while (getline(smessage, line))
			{
				if(regex_search(line, result, getPattern))
				{
					file= result[1];
					string version = result[2];
					//cout << result.size();
					cout << "File: " << file << "\nVersion: " << version << "\n";
				}
				else
				{
					cout << "line " << i << ": " << line << "\n";
				}
				if(line.length() == 0)
				{
					moveon++;
					cout << "\n\nLine is: " << line << " Count is: " << line.length() << "\n";
					break;
				}
				i++;
			}

			if(file == "/")
			{
				file = index;
			}

			string requestedFilePath;
			requestedFilePath.append(basedir);
			requestedFilePath.append(filedivider);
			requestedFilePath.append(file);

			cout << requestedFilePath << "\n";
			cout << "Sending info back now" << "\n";

			char q[1000];
			sprintf(q,"HTTP/1.0 200 OK\n",sizeof("HTTP/1.0 200 OK\n"));
			send(sd,q,sizeof("HTTP/1.0 200 OK\n"),0);
			/*sprintf(q,"Date: Fri, 31 Dec 1999 23:59:59 GMT");
			send(sd,q,1000,0);*/
			memset(q,0,sizeof(q));
			sprintf(q,"Date: Fri, 25 Feb 2011 23:59:59 GMT\n",sizeof("Date: Fri, 25 Feb 2011 23:59:59 GMT\n"));
			send(sd,q,sizeof("Date: Fri, 25 Feb 2011 23:59:59 GMT\n"),0);

			memset(q,0,sizeof(q));
			sprintf(q,"Content-Type: text/html\n",sizeof("Content-Type: text/html\n"));
			send(sd,q,sizeof("Content-Type: text/html\n"),0);
			
			memset(q,0,sizeof(q));

			const char* filenamechar = requestedFilePath.c_str();
			int filesize = FileSize64(filenamechar);
			sprintf(q,"Content-Length: %d",filesize);
			send(sd,q,strlen(q),0);
			
			memset(q,0,sizeof(q));
			sprintf(q,"\n\n",sizeof("\n\n"));
			send(sd,q,sizeof("\n\n"),0);

			ifstream myfile (requestedFilePath);
			if (myfile.is_open())
			{
				while ( myfile.good() )
				{
					getline (myfile,line);
					const char* p = line.c_str();
					send(sd,p,strlen(p),0);
					//memset(p,0,sizeof(p));
					cout << line << endl;
				}
				myfile.close();
			}
			
			else cout << "Unable to open file"; 
			
			cout << "ended file\n";
			
			sprintf(q,"\n",sizeof("\n"));
			send(sd,q,sizeof("\n"),0);

			return true;
    //        int nSentBytes = 0;
    //        while (nSentBytes < nReadBytes) {
				////int nTemp = send(sd, acReadBuffer + nSentBytes, nReadBytes - nSentBytes, 0);
    //            if (nTemp > 0) {
    //                cout << "Sent " << nTemp << 
    //                        " bytes back to client." << endl;
    //                nSentBytes += nTemp;
    //            }
    //            else if (nTemp == SOCKET_ERROR) {
    //                return false;
    //            }
    //            else {
    //                // Client closed connection before we could reply to
    //                // all the data it sent, so bomb out early.
    //                cout << "Peer unexpectedly dropped connection!" << 
    //                        endl;
    //                return true;
    //            }
    //        }
        }
        else if (nReadBytes == SOCKET_ERROR) {
            return false;
        }
    } while (nReadBytes != 0);

    cout << "Connection closed by peer." << endl;
    return true;
}

__int64 FileSize64( const char * szFileName ) 
{ 
  struct __stat64 fileStat; 
  int err = _stat64( szFileName, &fileStat ); 
  if (0 != err) return 0; 
  return fileStat.st_size; 
}